#pragma once
#include<iostream>
#include<algorithm>
#include<vector>
#include"BasicType.h"
#include"MemUtil.h"
#include"IO.h"
#include"ResourceInfo.h"
using namespace std;

enum OPTYPE{DEL, ADD, MOD};


//记录文件格式
//int64     指向可以继续写入记录的位置（用来保证acid）
//int64     指向未更新到数据库的第一条记录头部（考虑到顺序写 延后更新）
class Record{
public:
    
    string baseName;
    string tableName;
    string colName;
    int16_t blockNo;
    int opInd;
    OPTYPE opType;
    int32_t timeSeq; //序列化反序列化不加入创建序号 创建序号只用于优化
    Basic* oldData = nullptr;
    Basic* newData = nullptr;

    void initParam(const string& baseName, const string& tableName, const string& colName, const int16_t& blockNo,
                   const int& opInd, const OPTYPE& opType, Basic* oldData, Basic* newData){
        this->baseName = baseName;
        this->tableName = tableName;
        this->colName = colName;
        this->blockNo = blockNo;
        this->opInd = opInd;
        this->opType = opType;
        this->oldData = oldData;       //oldData在外部拷贝
        this->newData = newData;      //newData在外部拷贝
    }

    Record(const string& baseName, const string& tableName, const string& colName, const int16_t& blockNo, const int& opInd,
           const OPTYPE& opType, Basic* oldData, Basic* newData){
        initParam(baseName, tableName, colName, blockNo, opInd, opType, oldData, newData);
    }


    Record(const char* byteData){
        int ind = 0, nameMaxLen = IOUtil::getNameMaxLen();
        baseName = StringUtil::diskDataToMemStr(&byteData[ind], nameMaxLen);
        ind += nameMaxLen;
        tableName = StringUtil::diskDataToMemStr(&byteData[ind], nameMaxLen);;
        ind += nameMaxLen;
        colName = StringUtil::diskDataToMemStr(&byteData[ind], nameMaxLen);;
        ind += nameMaxLen;
        memcpy(&blockNo, &byteData[ind], sizeof (blockNo));
        ind += sizeof (blockNo);
        memcpy(&opInd, &byteData[ind], sizeof (opInd));
        ind += sizeof (opInd);
        memcpy(&opType, &byteData[ind], sizeof (opType));
        ind += sizeof (opType);
        if(opType == DEL){ return; }    //删除记录没有数据类型和数据信息
        //数据类型不在类内存储 但是反序列化数据需要读取
        TYPE dataType;
        memcpy(&dataType, &byteData[ind], sizeof (dataType));
        ind += sizeof (dataType);
        if(opType == MOD){      //只有MOD操作才有oldData
            oldData = BasicUtil::byteDataToBasicData(&byteData[ind], dataType);
        }
        ind += Basic::dataMaxLen;
        newData = BasicUtil::byteDataToBasicData(&byteData[ind], dataType);
    }


    Record(const Record& rec){
        initParam(rec.baseName, rec.tableName, rec.colName, rec.blockNo, rec.opInd, rec.opType,
                  BasicUtil::getCopyBasicData(rec.oldData), BasicUtil::getCopyBasicData(rec.newData));
    }


    //析构函数里面都是同步释放 仅在最头部控制异步
    ~Record(){
        MemUtil::clearPtrMem(oldData);
        MemUtil::clearPtrMem(newData);
    }

    void setByteData(char* byteData){
        int ind = 0, nameMaxLen = IOUtil::getNameMaxLen();
        memcpy(&byteData[ind], StringUtil::toByteData(StringUtil::memStrToDiskStr(baseName, nameMaxLen)), nameMaxLen);
        ind += nameMaxLen;
        memcpy(&byteData[ind], StringUtil::toByteData(StringUtil::memStrToDiskStr(tableName, nameMaxLen)), nameMaxLen);
        ind += nameMaxLen;
        memcpy(&byteData[ind], StringUtil::toByteData(StringUtil::memStrToDiskStr(colName, nameMaxLen)), nameMaxLen);
        ind += nameMaxLen;
        memcpy(&byteData[ind], &blockNo, sizeof (blockNo));
        ind += sizeof (blockNo);
        memcpy(&byteData[ind], &opInd, sizeof (opInd));
        ind += sizeof (opInd);
        memcpy(&byteData[ind], &opType, sizeof (opType));
        ind += sizeof (opType);
        if(opType == DEL){ return; }    //删除记录不需要写数据类型和数据
        //将数据类型写入磁盘 数据类型不在类内存储
        const TYPE& dataType = newData->getType();  //添加和修改newData一定不为null
        memcpy(&byteData[ind], &dataType, sizeof (dataType));
        ind += sizeof (dataType);
        if(oldData != nullptr){
            memcpy(&byteData[ind], oldData->getByteData(), Basic::dataMaxLen);
        }
        ind += Basic::dataMaxLen;
        if(newData != nullptr){
            memcpy(&byteData[ind], newData->getByteData(), Basic::dataMaxLen);
        }
    }

    static int getRecordSize(){
        //数据类型不在类内存储 但是需要写入磁盘
        return (IOUtil::getNameMaxLen() * 3) +
               sizeof (int16_t) + sizeof (int) + sizeof (OPTYPE) +
               sizeof (TYPE) + (Basic::dataMaxLen * 2);
    }

    static void setCreateTimeToRecords(vector<Record*>& recVec);

    static void addToNewRecords(vector<Record*>& newRecords, vector<Record*>& tmpRecords);

    static vector<Record*> getOptimizRecordVec(vector<Record*>& recVec, const bool& isSameRes);

    static char* getRecordsBuffer(const vector<Record*>& allRecords);

    static vector<Record*> getRecordVec(const char* byteData, const int64_t& dataLen);

    static vector<Record*> readRedoDataRecords();
};



//判断是否在同一数据块
bool isInSameRes(const Record* rec1, const Record* rec2){
    return rec1->baseName == rec2->baseName &&
           rec1->tableName == rec2->tableName &&
           rec1->colName == rec2->colName &&
           rec1->blockNo == rec2->blockNo;
}

//在同一数据块后判断是否同一操作下标
bool isSameInd(const Record* rec1, const Record* rec2){
    return rec1->opInd == rec2->opInd;
}

//判断操作的是否是同一数据
bool isSameDataLoc(const Record* rec1, const Record* rec2){
    return isInSameRes(rec1, rec2) && isSameInd(rec1, rec2);
}

//在同一数据块内的比较
bool sameResRecCmp(const Record* rec1, const Record* rec2){
    const int& opInd1 = rec1->opInd, opInd2 = rec2->opInd;
    if(opInd1 != opInd2){
        return opInd1 < opInd2;
    }
    const OPTYPE& opType1 = rec1->opType, opType2 = rec2->opType;
    if(opType1 != opType2){
        return opType1 < opType2;
    }
    return rec1->timeSeq < rec2->timeSeq;
}


//依据磁盘顺序排序操作
//仅用于操作记录里排序
bool recCmp(const Record* rec1, const Record* rec2) {
    const string& baseName1 = rec1->baseName, baseName2 = rec2->baseName;
    if(baseName1 != baseName2){
        return baseName1 < baseName2;
    }
    const string& tableName1 = rec1->tableName, tableName2 = rec2->tableName;
    if(tableName1 != tableName2){
        return tableName1 < tableName2;
    }
    const string& colName1 = rec1->colName, colName2 = rec2->colName;
    if(colName1 != colName2){
        return colName1 < colName2;
    }
    const int16_t& blockNo1 = rec1->blockNo, blockNo2 = rec2->blockNo;
    if(blockNo1 != blockNo2){
        return blockNo1 < blockNo2;
    }
    return sameResRecCmp(rec1, rec2);
}


class RecordUtil{
public:
    static ResourceInfo recordToResourceInfo(const Record* rec){
        const OPTYPE& opType = rec->opType;
        if(opType == DEL){
            return ResourceInfo(rec->baseName, rec->tableName, rec->blockNo);
        }
        else if(opType == ADD || opType == MOD){                                            //添加和修改时newData一定不为null
            return ResourceInfo(rec->baseName, rec->tableName, rec->colName, rec->blockNo, rec->newData->getType());
        }
        else{
            throw string("opType error");
        }
    }
};



void Record::setCreateTimeToRecords(vector<Record*>& recVec){
    int32_t ts = 0;
    for(Record* rec : recVec){
        rec->timeSeq = ts++;
    }
}


void Record::addToNewRecords(vector<Record*>& newRecords, vector<Record*>& tmpRecords){
    Record* firstRecord = tmpRecords.front();
    Record* lastRecord = tmpRecords.back();
    if(firstRecord == lastRecord){          //无需化简
        newRecords.push_back(firstRecord); //直接添加 tmpRec里的数据无需释放
        tmpRecords.clear();     //清空临时记录vec
    }
    else{
        OPTYPE firstType = firstRecord->opType;
        newRecords.push_back(new Record(firstRecord->baseName, firstRecord->tableName, firstRecord->colName, firstRecord->blockNo,
                                        lastRecord->opInd, firstType,
                                        firstRecord->oldData,
                                        lastRecord->newData));
        //转交数据所有权
        firstRecord->oldData = nullptr;
        lastRecord->newData = nullptr;
        MemUtil::asyncClearVecMem(tmpRecords);
        //列表可能立马就要使用 同步清空 里面记录异步释放
        tmpRecords.clear();
    }
}

//获取优化后的记录vec 旧vec中没有复用的record被释放
vector<Record*> Record::getOptimizRecordVec(vector<Record*>& recVec, const bool& isSameRes){
    if(recVec.empty()){ return {}; }
    //按照在vec的顺序为每一个rec赋序号
    setCreateTimeToRecords(recVec);
    //第一步优化，依据磁盘操作顺序对无序的记录排序。使随机读写转顺序读写。
    sort(recVec.begin(), recVec.end(), isSameRes ? sameResRecCmp : recCmp);
    //第二步优化，记录化简。如(先添加一条数据，再把该条数据修改为另一值)->(直接添加一条数据数据为修改修改后的值)
    vector<Record*> newRecords;
    vector<Record*> tmpRecords;
    for(Record* tmpRecord : recVec){
        if(!tmpRecords.empty() &&
           !(isSameRes ? isSameInd(tmpRecords.back(), tmpRecord) : isSameDataLoc(tmpRecords.back(), tmpRecord)))
        {
            addToNewRecords(newRecords, tmpRecords);    //调用时所有对同一数据的修改都按序在tmpRec里
        }
        tmpRecords.push_back(tmpRecord);
    }
    addToNewRecords(newRecords, tmpRecords);
    return newRecords;
}

char* Record::getRecordsBuffer(const vector<Record*> &allRecords){
    const int32_t& recordSize = Record::getRecordSize();
    char* byteData = new char[allRecords.size() * recordSize];
    int64_t ind = 0;
    for(Record* record : allRecords){
        record->setByteData(&byteData[ind]);
        ind += recordSize;
    }
    return byteData;
}

vector<Record*> Record::getRecordVec(const char* byteData, const int64_t& dataLen){
    vector<Record*> recVec;
    const int32_t& recSize = Record::getRecordSize();
    for(int64_t ind = 0; ind < dataLen; ind += recSize){
        recVec.push_back(new Record(&byteData[ind]));
    }
    return recVec;
}

vector<Record*> Record::readRedoDataRecords(){
    fstream f;
    try {
        f = IOUtil::getGoodFstream(IOUtil::getRecordPath(), ios::in | ios::out | ios::binary);
    }  catch (string) {
        return {};
    }
    const pair<int64_t, int64_t>& metaInfo = IOUtil::readPairMetaInfo(f);
    const int64_t& increWriteProgress = metaInfo.second;
    const int64_t& dataLen = metaInfo.first - increWriteProgress;
    char* buffer = new char[dataLen];
    f.seekg(increWriteProgress, ios::beg);
    f.read(buffer, dataLen);
    const vector<Record*>& recVec = Record::getRecordVec(buffer, dataLen);
    MemUtil::asyncClearPtrArrMem(buffer);
    return recVec;
}






